/* Created on Jul 11, 2007 */
/**
 * 
 */
package com.xbang.core.dao.search.component;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.xbang.core.utils.SearchUtils;
import com.xbang.core.utils.StringUtils;

/**
 * @author dev.zhang
 */
public class SearchComponent implements Serializable {

	private static final long serialVersionUID = 3146234628003632396L;

	private Map<String, String> whereSentences = new HashMap<String, String>();

	private List<Map<String, String>> orderSentences = new ArrayList<Map<String, String>>();

	private List<Map<String, String>> groupSentences = new ArrayList<Map<String, String>>();

	private Map<String, Object> parameters = new HashMap();

	private Map<String, Object> orderParameters = new HashMap();

	/** search parameter by chiness. this property need set annotation note. */
	private Map<String, String> searchParameterNode = new HashMap<String, String>();

	/**
	 * The default constructor
	 */
	public SearchComponent() {

	}

	/**
	 * 
	 * @param searchElements
	 */
	public SearchComponent(final Map<String, Object> searchElements) {
		addSearchElements(searchElements);
	}

	public List<Map<String, String>> getGroupSentences() {
		return groupSentences;
	}

	/**
	 * 
	 * @param searchElements
	 * @return
	 */
	public SearchComponent addSearchElements(final Map<String, Object> searchElements) {
		for (Iterator<Map.Entry<String, Object>> iter = searchElements.entrySet().iterator(); iter.hasNext();) {
			Map.Entry<String, Object> entry = (Map.Entry<String, Object>) iter.next();
			this.addSearchElement(entry.getKey(), entry.getValue());
		}
		return this;
	}

	public SearchComponent addSearchComponentContext(SearchComponent searchComponent) {

		this.groupSentences.addAll(searchComponent.groupSentences);
		this.orderSentences.addAll(searchComponent.orderSentences);
		this.parameters.putAll(searchComponent.parameters);
		this.whereSentences.putAll(searchComponent.whereSentences);
		this.orderParameters.putAll(searchComponent.orderParameters);

		return this;
	}

	/**
	 * Add a searchElement to the searchComponent according to the key and
	 * parameter, which includes a dynamic sentence and dynamic parameter
	 * 
	 * @param key
	 *            The key of the searchElement
	 * @param parameter
	 *            The parameter of the searchElement, which is a type safe
	 *            object
	 * @return
	 */
	public SearchComponent addSearchElement(String key, Object parameter) {

		SearchContext searchContext = new SearchContext(key);

		// determine which kind of sentence the element belongs to
		if (searchContext.isGroupSentence()) {
			this.addGroupSentence(searchContext, parameter);
		} else {

			// filter those parameters which is null
			if (parameter != null) {

				if (searchContext.isOrderSentence()) {
					this.addOrderSentence(searchContext, parameter);
					// add to order parameter map
					this.addOrderParameter(searchContext.getTarget(), parameter);
				} else {
					this.addWhereSentence(searchContext, parameter);
					// add to parameter map
					this.addParameter(searchContext.getParameterKey(), parameter);
				}

			} else { // when parameter is null, determine whether
				// searchComponent contains the searchCriteria and do
				// remove

				if (whereSentences.containsKey(key)) {
					this.whereSentences.remove(key);
					this.parameters.remove(searchContext.getParameterKey());
				}
				for (Map<String, String> orderSentence : orderSentences) {
					if (orderSentence.containsKey(key)) {
						this.orderSentences.remove(key);
						this.orderParameters.remove(searchContext.getTarget());
					}
				}
			}

		}

		return this;
	}

	/**
	 * Reset order sentence
	 */
	public void resetOrderSentence() {
		this.orderParameters = new HashMap<String, Object>();
		this.orderSentences = new ArrayList<Map<String, String>>();
	}

	/**
	 * Reset group sentence
	 */
	public void resetGroupSentence() {
		this.groupSentences = new ArrayList<Map<String, String>>();
	}

	/**
	 * Reset where sentence
	 */
	public void resetWhereSentence() {
		this.whereSentences = new HashMap<String, String>();
		this.parameters = new HashMap<String, Object>();
	}

	/**
	 * Get final HQL count sentence according to the sentenceTemplate and using
	 * searchComponent as basic dependency
	 * 
	 * @param sentenceTemplate
	 * @param dynamic
	 * @return
	 */
	public String getHQLCountSentence(String sentenceTemplate, boolean dynamic) {
		String template = StringUtils.trim(sentenceTemplate);
		template = StringUtils.replacePlaceHolder(template, getSentenceMap());
		if (dynamic && !StringUtils.hasPlaceHolder(template)) {
			template += generateWhereSentence(template);
		}
		template += generateGroupSentence(template);
		return SearchUtils.getHQLCountSentence(template);
	}

	/**
	 * Get final SQL count sentence according to the sentenceTemplate and using
	 * searchComponent as basic dependency
	 * 
	 * @param sentenceTemplate
	 * @param dynamic
	 * @return
	 */
	public String getSQLCountSentence(String sentenceTemplate, boolean dynamic) {
		String template = StringUtils.trim(sentenceTemplate);
		template = StringUtils.replacePlaceHolder(template, getSentenceMap());
		if (dynamic && !StringUtils.hasPlaceHolder(template)) {
			template += generateWhereSentence(template);
		}
		template += generateGroupSentence(template);
		return SearchUtils.getSQLCountSentence(template);
	}

	/**
	 * Get final sentence according to the sentenceTemplate and using
	 * searchComponent as basic dependency
	 * 
	 * @param sentenceTemplate
	 *            The sentenceTemplate used to build the final sentence
	 * @param dynamic
	 *            The indicator whether to add dynamic sentences to the end of
	 *            the sentenceTemplate
	 * @return
	 */
	public String getSearchSentence(String sentenceTemplate, boolean dynamic) {
		String template = StringUtils.trim(sentenceTemplate);
		template = StringUtils.replacePlaceHolder(template, getSentenceMap());
		if (dynamic && !StringUtils.hasPlaceHolder(template)) {
			template += generateWhereSentence(template);
		}
		template += generateGroupSentence(template);
		template += generateOrderSentence(template);
		return template;
	}

	/**
	 * @return the parameters
	 */
	public Map<String, Object> getParameters() {
		return parameters;
	}

	/**
	 * 
	 * @return
	 */
	public Map<String, Object> getReturnedParameters() {
		Map<String, Object> params = new HashMap<String, Object>();
		for (Iterator<Map.Entry<String, Object>> iter = parameters.entrySet().iterator(); iter.hasNext();) {
			Map.Entry<String, Object> entry = iter.next();
			if (entry.getKey().startsWith("like")) {
				String value = (String) entry.getValue();
				params.put(entry.getKey(), value.substring(1, value.length() - 1));
			} else {
				params.put(entry.getKey(), entry.getValue());
			}
		}
		return params;
	}

	/**
	 * @return the orderParameters
	 */
	public Map<String, Object> getOrderParameters() {
		return orderParameters;
	}

	public List<Map<String, String>> getOrderSentences() {
		return this.orderSentences;
	}

	/**
	 * 
	 * @return
	 */
	public Map<String, Object> getAliasOrderParameters() {
		Map<String, Object> aliasOrderParameters = new HashMap<String, Object>();
		if (this.getOrderParameters() != null) {
			for (Iterator<Map.Entry<String, Object>> iter = this.getOrderParameters().entrySet().iterator(); iter.hasNext();) {
				Map.Entry<String, Object> entry = iter.next();
				if (entry.getKey().indexOf(".") != -1) {
					aliasOrderParameters.put(entry.getKey().substring(0, entry.getKey().indexOf(".")), entry.getValue());
				}
			}
		}
		return aliasOrderParameters;
	}

	/**
	 * Returns the sentenceMap including where sentence and order sentence
	 * 
	 * @return
	 */
	public Map<String, String> getSentenceMap() {
		Map<String, String> sentenceMap = new HashMap<String, String>();
		sentenceMap.putAll(whereSentences);
		return sentenceMap;
	}

	/**
	 * Generate whereSentence using sentence template
	 * 
	 * @param sentenceTemplate
	 * @return
	 */
	private String generateWhereSentence(String sentenceTemplate) {
		StringBuffer sb = new StringBuffer();
		for (Iterator iter = this.whereSentences.values().iterator(); iter.hasNext();) {
			sb.append(iter.next());
		}
		return sb.toString();
	}

	/**
	 * Generate orderSentence using sentence template
	 * 
	 * @param sentenceTemplate
	 * @return
	 */
	private String generateOrderSentence(String sentenceTemplate) {
		StringBuffer sb = new StringBuffer();
		if (!this.orderSentences.isEmpty()) {
			sb.append(" ORDER BY ");
		}
		for (Map<String, String> orderSentence : this.orderSentences) {
			for (Iterator iter = orderSentence.values().iterator(); iter.hasNext();) {
				if (sb.toString().toLowerCase().endsWith("asc") || sb.toString().toLowerCase().endsWith("desc")) {
					sb.append(",");
				}
				sb.append(iter.next());
				if (iter.hasNext()) {
					sb.append(",");
				}
			}
		}
		return sb.toString();
	}

	/**
	 * Generate groupSentence using sentence template
	 * 
	 * @param sentenceTemplate
	 * @return
	 */
	private String generateGroupSentence(String sentenceTemplate) {
		StringBuffer sb = new StringBuffer();
		if (!this.groupSentences.isEmpty()) {
			sb.append(" GROUP BY ");
		}
		
		for (int index = 0; index < this.groupSentences.size(); index++) {
			Map<String, String> groupSentence = this.groupSentences.get(index);
			for (Iterator iter = groupSentence.values().iterator(); iter.hasNext();) {
				sb.append(iter.next());
				if (iter.hasNext()) {
					sb.append(",");
				}
			}
			if (index < this.groupSentences.size() - 1) {
				sb.append(",");
			}
		}
		return sb.toString();
	}

	/**
	 * @return the whereSentences
	 */
	public Map<String, String> getWhereSentences() {
		return whereSentences;
	}

	/**
	 * @param whereSentences
	 *            the whereSentences to set
	 */
	public void setWhereSentences(Map<String, String> whereSentences) {
		this.whereSentences = whereSentences;
	}

	/**
	 * @param parameters
	 *            the parameters to set
	 */
	public void setParameters(Map<String, Object> parameters) {
		this.parameters = parameters;
	}

	/**
	 * @param orderParameters
	 *            the orderParameters to set
	 */
	public void setOrderParameters(Map<String, Object> orderParameters) {
		this.orderParameters = orderParameters;
	}

	/**
	 * Used internal to add where sentence to the searchComponent
	 * 
	 * @param searchContext
	 * @param parameter
	 */
	private void addWhereSentence(SearchContext searchContext, Object parameter) {
		this.whereSentences.put(searchContext.getKey(), searchContext.getSentence(parameter));
	}

	/**
	 * Used internal to add order sentence to the searchComponent
	 * 
	 * @param searchContext
	 * @param sentence
	 */
	private void addOrderSentence(SearchContext searchContext, Object parameter) {
		Map<String, String> orderSentence = new HashMap<String, String>();
		orderSentence.put(searchContext.getKey(), searchContext.getSentence(parameter));
		this.orderSentences.add(orderSentence);
	}

	/**
	 * Used internal to add group sentence to the searchComponent
	 * 
	 * @param searchContext
	 * @param parameter
	 */
	private void addGroupSentence(SearchContext searchContext, Object parameter) {
		Map<String, String> groupSentence = new HashMap();
		groupSentence.put(searchContext.getKey(), searchContext.getSentence(parameter));
		this.groupSentences.add(groupSentence);
	}

	/**
	 * Used internal to add dynamic parameter to the searchComponent
	 * 
	 * @param parameterKey
	 * @param parameter
	 */
	private void addParameter(String parameterKey, Object parameter) {
		this.parameters.put(parameterKey, parameter);
	}

	/**
	 * Used internal to add order parameter to the searchComponent
	 * 
	 * @param orderParameterKey
	 * @param parameter
	 */
	private void addOrderParameter(String orderParameterKey, Object parameter) {
		this.orderParameters.put(orderParameterKey, parameter);
	}

	/**
	 * @return the searchParameterNode
	 */
	public Map<String, String> getSearchParameterNode() {
		return searchParameterNode;
	}

	/**
	 * @param searchParameterNode
	 *            the searchParameterNode to set
	 */
	public void setSearchParameterNode(Map<String, String> searchParameterNode) {
		this.searchParameterNode = searchParameterNode;
	}

}
